#include "aggAux.h"
#include "manager.h"
#include <cmath>
#include <cassert>

// Performs the treatment of special cases
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// IN: aggMesh, setOfDomains, iMesh
// OUT: aggMesh

void firstCase(pMesh Mesh, set<int> &setOfDomains);
void secondCase(pMesh Mesh, set<int> &setOfDomains);
double calcDist (double coord[], double coordNeighbour[]);

void aggSC(pMesh theMesh, set<int> &setOfDomains, vector<pMesh> aggMesh, int iMesh)
{

cout<<"   Treating Special Cases... ";

// 1st Special Case: the SuperNode has just one connection
firstCase(aggMesh.at(iMesh), setOfDomains);

// 2nd Special Case: the SuperNode was not agglomerated in the fine mesh
secondCase(aggMesh.at(iMesh), setOfDomains);

// Getting number of super nodes
int iSN = M_numVertices(aggMesh.at(iMesh));
int iSE = M_numEdges(aggMesh.at(iMesh));

cout << iSN <<" nodes in the "<< iMesh <<"° coarse mesh after Special Cases  treatment\n";
cout <<"                             ";
cout << iSE <<" edges in the "<< iMesh <<"° coarse mesh after Special Cases  treatment\n";

}

void firstCase (pMesh aggMesh, set<int> &setOfDomains)
{// 1st Special Case: the SuperNode has just one connection

    // Definig the domain flag
set<int>::iterator iter = setOfDomains.begin();

char volume_string[64];

// Loop on the seed priority queue for this level
VIter vit = M_vertexIter(aggMesh);
while (pEntity node = VIter_next(vit))
{
    int numEdges = V_numEdges(node);        // get number of edges surrounding
                                            // the entity vertex
    // SuperNode has just one connection
    if (numEdges == 1)
    {// Agglomerate the superNode to its unique connection

        pEdge edge = V_edge(node,0);

        // Looking for boundary flag
        int isBoundary(0);
		EN_getDataInt (edge, MD_lookupMeshDataId("isBoundary"), &isBoundary);
		if (!isBoundary)
        { // Internal node
            pVertex neighbourNode;
            if (node == E_vertex(edge, 0))
            {
                neighbourNode = E_vertex(edge, 1);
            }else
            {
                neighbourNode = E_vertex(edge, 0);
            }

            // (DEV) -  VERIFICAR COMO OBTER gEntity!!
            pGEntity gEntity;
            //sprintf(volume_string,"volume-%d",GEN_tag(gEntity));
            sprintf(volume_string,"volume-%d",*iter);

            double seedVolumeErased(.0), seedVolume(.0);
            EN_getDataDbl(node,MD_lookupMeshDataId(volume_string),&seedVolumeErased);
            EN_getDataDbl(neighbourNode,MD_lookupMeshDataId(volume_string),&seedVolume);

            seedVolume += seedVolumeErased;

            EN_attachDataDbl(neighbourNode, MD_lookupMeshDataId(volume_string), seedVolume);

            // (DEV) - deleting from database AND the pointer (with del(lowercase), delete just from database)
            aggMesh->DEL(node);
            aggMesh->DEL(edge);
        }else
        { // (DEV) - Do it for boundary nodes
        }

    }

}
}

void secondCase (pMesh aggMesh, set<int> &setOfDomains)
{// 2nd Special Case: the SuperNode was not agglomerated in the fine mesh

// Definig the domain flag
set<int>::iterator iter = setOfDomains.begin();

char volume_string[64];

// Loop on the seed priority queue for this level
VIter vit = M_vertexIter(aggMesh);
while (pEntity node = VIter_next(vit))
{
    int aggStatus = 0;
    EN_getDataInt (node, MD_lookupMeshDataId("aggStatus"), &aggStatus);
    if(!aggStatus)
    {//Supernode not agglomerated

        // Getting coordinates of the supernode
        double coord[3] = {.0, .0, .0};
        V_coord(node, coord);

        double minDist(1.0E+10);
        int pos(-1);

        vector<pEntity> neighbourNode;
        int numEdges = V_numEdges(node);        // get number of edges surrounding
        for (int i=0; i<numEdges; i++)          // loop over all edges using vertex
      	{
           	pEdge edge = V_edge(node,i);
           	if (node == E_vertex(edge, 0))
           	{
           		neighbourNode.push_back(E_vertex(edge, 1));
           	}else
           	{
           		neighbourNode.push_back(E_vertex(edge, 0));
      		}
      		// Looking for boundary flag
            int isBoundary(0);
            EN_getDataInt (edge, MD_lookupMeshDataId("isBoundary"), &isBoundary);
            if (!isBoundary) aggMesh->DEL(edge); // (DEV) We still have to transfer the coefficients data to
						 // the new superEdges
						 // Delete all the connectivities of the supernode, which will
                                                 // be erase later
      	}
        // Loop on neighbours to calculate the neighbour within minimal distance
        for (int i =0; i<neighbourNode.size(); i++)
        {
            double coordNeighbour[3] = {.0, .0, .0};
            V_coord(neighbourNode.at(i), coordNeighbour);
            double dist = calcDist(coord,coordNeighbour);
            if (dist<=minDist)
            {
                minDist = dist;
                pos = i;
            }
        }

        assert(pos>=0); // Test for legal position
        // Agglomerate the supernode to this neighbour and delete all its connectivities
        // (DEV) -  VERIFICAR COMO OBTER gEntity!!
        pGEntity gEntity;
        //sprintf(volume_string,"volume-%d",GEN_tag(gEntity));
        sprintf(volume_string,"volume-%d",*iter);

        double seedVolumeErased(.0), seedVolume(.0);
        EN_getDataDbl(node,MD_lookupMeshDataId(volume_string),&seedVolumeErased);
        EN_getDataDbl(neighbourNode.at(pos),MD_lookupMeshDataId(volume_string),&seedVolume);

        seedVolume += seedVolumeErased;

        EN_attachDataDbl(neighbourNode.at(pos), MD_lookupMeshDataId(volume_string), seedVolume);

        // (DEV) - deleting from database AND the pointer (with del(lowercase), delete just from database)
        aggMesh->DEL(node);
    }
}
}
double calcDist (double coord[], double coordNeighbour[])
{ // Calculate distance between two nodes
    double dist(0.0);
    for (int i =0; i<3; i++)
    {
        dist += (coord[i] - coordNeighbour[i])*(coord[i] - coordNeighbour[i]);
    }
    dist = sqrt(dist);
    return dist;
}
